﻿using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Channels;
using TIBCO.Rendezvous;
using Message = System.ServiceModel.Channels.Message;

namespace Tibco.ServiceModel
{
    internal sealed class TibcoRequestChannel : TibcoRequestChannelBase
    {
        private TibcoTransportBindingElement m_bindingElement;

        private MessageEncoder m_encoder;

        private CMTransport m_cmTransport;

        public TibcoRequestChannel(BindingContext context, CMTransport transport, EndpointAddress address) : base(context, address)
        {
            this.m_bindingElement = context.Binding.Elements.Find<TibcoTransportBindingElement>();
            var messageEncodingBindingElement = context.Binding.Elements.Find<MessageEncodingBindingElement>();
            if (messageEncodingBindingElement != null)
            {
                this.m_encoder = messageEncodingBindingElement.CreateMessageEncoderFactory().Encoder;
            }
            this.m_cmTransport = transport;
        }

        public override void Close(TimeSpan timeout)
        {
            if (base.State == CommunicationState.Closed || base.State == CommunicationState.Closing)
            {
                return;
            }

            m_cmTransport?.Destroy();
            m_cmTransport = null;

            base.OnClosing();
            base.OnClosed();
        }

        public override void Open(TimeSpan timeout)
        {
            if (base.State != CommunicationState.Created && base.State != CommunicationState.Closed)
            {
                throw new InvalidOperationException(string.Format("Cannot open the channel from the {0} state.", base.State));
            }
            base.OnOpening();
            base.OnOpened();
        }

        public override Message Request(Message message, TimeSpan timeout)
        {
            Message uri = null;

            if (message.State != MessageState.Closed)
            {
                try
                {
                    byte[] array = null;
                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        this.m_encoder.WriteMessage(message, memoryStream);
                        array = memoryStream.ToArray();
                    }

                    var s = System.Text.Encoding.UTF8.GetString(array, 0, array.Length);
                    var msg = new TIBCO.Rendezvous.Message();
                    msg.SendSubject = base.RemoteAddress.Uri.PathAndQuery.Replace("/", "").Replace("-", "");
                    msg.AddField("DATA", s);

                    if (message.Headers.ReplyTo != null)
                    {
                        var replyMsg = this.m_cmTransport.SendRequest(msg, timeout.TotalSeconds);

                        Debug.Out("TibcoRequestChannel.Request()", s);

                        if (replyMsg != null)
                        {
                            var data = (string)replyMsg.GetField("DATA");
                            var bytes = System.Text.Encoding.UTF8.GetBytes(data);
                            uri = this.m_encoder.ReadMessage(new MemoryStream(bytes), (int)this.m_bindingElement.MaxReceivedMessageSize);

                            Debug.Out("TibcoRequestChannel.Request() - Reply Message", data);
                        }
                        else
                        {
                            Debug.Out("TibcoRequestChannel.Request()", "Request Timeout!");
                        }
                    }
                    else
                    {
                        this.m_cmTransport.Send(msg);
                        Debug.Out("TibcoRequestChannel.Request() - (no reply)", s);
                    }
                }
                catch(Exception ex)
                {
                    Debug.Out("TibcoRequestChannel.Request() Exception", ex.Message);
                }
            }

            return uri;
        }
    }
}